package cn.edu.sjtu.stap.cfg.builder;

import java.util.*;

import org.aspectj.ajdt.internal.compiler.ast.*;
import org.aspectj.org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.*;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.*;

import cn.edu.sjtu.stap.cfg.*;
import cn.edu.sjtu.stap.graph.*;

/**
 * Implemetation of intra-module CFG builder for the default CFG builder. It
 * extends ASTVisitor and traverses the AST in the way which conforms to that
 * proposed by AJC. It traverses the given AST, and build intra-module CFGs when
 * encountering methods, constructors, advices and other method-like modules.
 * All the intra-module CFGs are stored in a map, and can be fetched after the
 * AST have been traversed. In a module, all the statements are represented by
 * nodes, and execution order is represented by flows. Typically, the structure
 * in a module can be considered as one of the following:
 * <ul>
 * <li>Execute by order</li>
 * <li>Branch</li>
 * <li>Loop</li>
 * </ul>
 * That is to say all the statements can be represented with these structures.
 * The granularity is statement (including common statements and expressions
 * that can form statements).
 * 
 * @author William
 * 
 */
public class IntraModuleCfgBuilder extends ASTVisitor {
    /**
     * All the intra-module CFGs generated by this visitor.
     */
    private HashMap<AbstractMethodDeclaration, ControlFlowGraph> cfgs = null;

    /**
     * Path of the current AST node from the AST root.
     */
    private Stack<ASTNode> path = null;

    /**
     * Keep the program trace, which is used for method pairs <code>visit</code>
     * and <code>endVisit</code> to keep info that should be passed between
     * them.
     */
    private ProgramTrace trace = new ProgramTrace();

    /**
     * The node that was just generated, which flows into current node. That is
     * to say the node will be connected with current node while processing
     * current node.
     */
    private CfgNode lastNode = null;

    /**
     * The end node of current structure. In most time, it is the exit node of
     * the CFG of current method. When the flow is in a branch, the current end
     * will be the auxiliary node that summarize all the flows from each branch.
     * Because the loop is represented as a branch with a loop back flow, so the
     * current end for loops is similar to that for branches.
     */
    private CfgNode currentEnd = null;

    /**
     * Intra-module CFG for current method.
     */
    private ControlFlowGraph currentCfg = null;

    /**
     * Default constructor.
     * 
     */
    public IntraModuleCfgBuilder() {
        initialize();
    }

    /**
     * Initialize this builder.
     * 
     */
    public void initialize() {
        cfgs = new HashMap<AbstractMethodDeclaration, ControlFlowGraph>();
        path = new Stack<ASTNode>();
        trace = new ProgramTrace();
        lastNode = null;
        currentEnd = null;
        currentCfg = null;
    }

    /**
     * Get the intra-module CFGs generated from the given AST (such as a
     * CompilationUnitDeclaration).
     * 
     * @return Map of intra-module CFGs generated after traversing the given
     *         AST.
     */
    public Map<AbstractMethodDeclaration, ControlFlowGraph> getResult() {
        return cfgs;
    }

    /**
     * Trace-keeper for traverse.
     * 
     * @author William
     * 
     */
    private class ProgramTrace {
        /**
         * Store common CFG node for common statement, and condition CFG node
         * for branch and loop.
         * 
         * Prefetched nodes are nodes in intra-module CFGs. They are generated
         * in method <code>visit</code>, and connected with other nodes of
         * that intra-module CFG in method <code>endVisit</code>. To keep the
         * node between the two methods, the stack it used.
         */
        Stack<CfgNode> prefetched = new Stack<CfgNode>();

        /**
         * Keep the nodes that have transfer flows, such as <code>break</code>,
         * <code>continue</code>. After the blocks (especially loops) finish,
         * they are connected with corresponding nodes correctly.
         */
        Stack<CfgNode> transfer = new Stack<CfgNode>();

        /**
         * Deal with the <code>return</code> statement. All the nodes that
         * represent return statement are kept, and after a method finishes,
         * they are connected with the exit of method.
         */
        Stack<CfgNode> returning = new Stack<CfgNode>();

        /**
         * Keep last intra-module CFG that was processing. It is used to deal
         * with the class definition in a module. The class definition may
         * contain method declarations, which is also needed to build
         * intra-module CFG for.
         */
        Stack<ControlFlowGraph> lastCfgs = new Stack<ControlFlowGraph>();

        // TODO for exception handling: Stack<CfgNode> throwing = new
        // Stack<CfgNode>();

        /**
         * Begin a branch +1, finish a branch -1.
         */
        int branchCount = 0;

        /**
         * Begin a loop +1, finish a loop -1.
         */
        int loopCount = 0;
    }

    /**
     * Add a node between the
     * <code>from<code> node and <code>to</code> node. First,
     * it will be removed the edge from <code>from</code> node to <code>to</code> node.
     * And make an edge from <from>from</code> node to <code>node</code> node, and an edge
     * from <code>node</code> node to <code>to</code> node. The kind of the newly created
     * edge which is from <code>from</code> node to <code>node</code> node is the same with
     * removed edge, and the kind of the other newly created edge will be normal.
     * 
     * @param node The node to add.
     * @param from The from node.
     * @param to The to node.
     */
    private void addNode(CfgNode node, CfgNode from, CfgNode to) {
        currentCfg.add(node);
        if (lastNode != null) {
            // if (from == to) {
            // System.err.println(node);
            // System.exit(1);
            // replace(currentCfg.getCfgExit(), node);
            // // CfgNode.swap(currentCfg.getCfgExit(), node);
            // currentCfg.addFlow(node, currentCfg.getCfgExit());
            // } else {
            CfgFlow f = currentCfg.getFlow(from, to);
            if (f != null) {
                currentCfg.removeFlow(f);
                currentCfg.addFlow(f.getKind(), from, node);
                currentCfg.addFlow(node, to);
            }
            // }
            lastNode = node;
        }
    }

    /**
     * Add a pair of call-and-return nodes.
     * 
     */
    private void addCallNode() {
        CfgNode callNode = trace.prefetched.pop();
        CfgNode returnNode = trace.prefetched.pop();

        addNode(callNode, lastNode, currentEnd);
        addNode(returnNode, lastNode, currentEnd);
        currentCfg.removeFlow(callNode, returnNode);
    }

    /**
     * Remove a node from CFG of current method. It reconnects the nodes that
     * have outgoing edges to the given node and the nodes that have incoming
     * edges from the given node. The newly created edge during reconnection has
     * the same kind as that of the removed incoming edge of the given node.
     * However, the situation that the node(s) that out-goes to the given node
     * and the node(s) that in-comes from the given node are both more than 1
     * will never happen, so if such a situation occurs, there might be an
     * error, and an exception will be thrown.
     * 
     * @param node
     *            CfgNode to remove from intra-module CFG of current method.
     * @throws IllegalArgumentException
     *             if the incoming edges and outgoing edges of the given node
     *             are both more than 1.
     */
    private void removeNode(CfgNode node) {
        if (node.getIncomingEdgeSet().size() > 1 && node.getOutgoingEdgeSet().size() > 1) {
            throw new IllegalArgumentException("Both incoming edge set and "
                    + "outgoing edge set have more than one element! Match error!");
        }

        ArrayList<GraphEdge> flows = new ArrayList<GraphEdge>();
        Iterator<GraphEdge> i = node.getIncomingEdgeSet().iterator();
        while (i.hasNext()) {
            flows.add(i.next());
        }
        i = flows.iterator();
        while (i.hasNext()) {
            CfgFlow edge = (CfgFlow) i.next();
            CfgNode from = (CfgNode) edge.getSourceNode();
            Iterator<GraphNode> tmp = node.getDestinationNodeSet().iterator();
            while (tmp.hasNext()) {
                CfgNode dNode = (CfgNode) tmp.next();
                currentCfg.addFlow(edge.getKind(), from, dNode);
            }
        }

        currentCfg.remove(node);
    }

    /**
     * Remove all the auxiliary nodes in the given control flow graph. It uses
     * <code>removeNode()</code> to remove the auxiliary nodes, so after
     * removing those nodes, the remaining nodes will be connected again.
     * 
     * @param g
     *            The intra-module CFG whose auxiliary nodes needs to be
     *            removed.
     */
    private void clearAuxiliaryNodes(ControlFlowGraph g) {
        Iterator<GraphNode> i = g.getNodes().iterator();

        while (i.hasNext()) {
            CfgNode n = (CfgNode) i.next();
            if (n instanceof CfgAuxiliaryNode) {
                removeNode(n);
            }
        }
    }

    /**
     * November 8, 2007: Add an auxilary node as the entry of the branch.
     * Because the condition expression may contain calls, so the only node for
     * condition may be not adequate. As a result, the auxiliary node is added
     * to denote the starting point of the branch. First, it add an auxiliary
     * node, then traverse the condition, and add the whole condition as the
     * node leading the true and false flows. The whole condition node is
     * returned. And the auxiliary node that denotes branch entry is pushed into
     * the stack <code>trace.prefetched</code>.
     * 
     * @param expression
     *            condition of the branch.
     * @param scope
     *            the scope where the expression is.
     * @return CfgNode that denotes the condition node leading the true and
     *         false flows. Notice it is not the entry node of the branch.
     */
    private CfgNode startBranch(Statement expression, BlockScope scope) {
        CfgAuxiliaryNode branchEntry = new CfgAuxiliaryNode(CfgNodeKind.AUXILIARY_NODE);
        addNode(branchEntry, lastNode, currentEnd);

        if (expression != null) {
            expression.traverse(this, scope);
        }
        CfgExpression cond = new CfgExpression(CfgNodeKind.BRANCH_EXPRESSION);
        cond.setASTNode(expression);
        addNode(cond, lastNode, currentEnd);

        // Add an auxiliary node for the end of the branch.
        CfgAuxiliaryNode branchExit = new CfgAuxiliaryNode(CfgNodeKind.AUXILIARY_NODE);
        currentCfg.removeFlow(cond, currentEnd);
        currentCfg.addFlow(cond, branchExit);
        currentCfg.addFlow(branchExit, currentEnd);

        // Add an auxiliary node for true flow.
        CfgAuxiliaryNode trueNode = new CfgAuxiliaryNode(CfgNodeKind.AUXILIARY_NODE);
        currentCfg.removeFlow(cond, branchExit);
        currentCfg.addFlow(CfgFlowKind.TRUE_FLOW, cond, trueNode);
        currentCfg.addFlow(trueNode, branchExit);

        // Add an auxiliary node for false flow.
        CfgAuxiliaryNode falseNode = new CfgAuxiliaryNode(CfgNodeKind.AUXILIARY_NODE);
        // currentCfg.removeFlow(cond, ifExit);
        currentCfg.addFlow(CfgFlowKind.FALSE_FLOW, cond, falseNode);
        currentCfg.addFlow(falseNode, branchExit);

        lastNode = trueNode;
        trace.prefetched.push(currentEnd);
        currentEnd = branchExit;
        trace.prefetched.push(branchExit);
        trace.prefetched.push(branchEntry);
        // trace.falseNodes.push(falseStatement);
        // trace.trueNodes.push(trueStatement);
        trace.branchCount++;

        return cond;
    }

    private void endBranch() {
        trace.prefetched.pop();
        lastNode = trace.prefetched.pop();
        currentEnd = trace.prefetched.pop();
        // trace.falseNodes.pop();
        // trace.trueNodes.pop();
        trace.branchCount--;
    }

    private void connectTransfer(CfgNode continueNode, CfgNode breakNode) {
        Iterator<GraphEdge> i = null;
        ArrayList<GraphEdge> toRemove = new ArrayList<GraphEdge>();

        // Remove all normal edges of transfer node, such <code>break</code>,
        // <code>continue</code>.
        // CfgNode cond = loopInfo.loops.pop();
        // CfgNode node = loopInfo.loopTransfer.pop();
        CfgNode branchEntry = trace.prefetched.pop();
        CfgNode node = trace.transfer.pop();
        while (node != branchEntry) {
            i = node.getOutgoingEdgeSet().iterator();
            toRemove.clear();
            while (i.hasNext()) {
                GraphEdge edge = i.next();
                // if (!edge.getDestinationNode().equals(cond)) {
                toRemove.add(edge);
                // }
            }
            i = toRemove.iterator();
            while (i.hasNext()) {
                currentCfg.remove(i.next());
            }

            // Make the transfer edges for the transfer nodes.
            if (node.getKind() == CfgNodeKind.BREAK_STATEMENT) {
                currentCfg.addFlow(CfgFlowKind.BREAK_FLOW, node, breakNode);
            } else if (node.getKind() == CfgNodeKind.CONTINUE_STATEMENT) {
                currentCfg.addFlow(CfgFlowKind.CONTINUE_FLOW, node, continueNode);
            }

            // node = loopInfo.loopTransfer.pop();
            node = trace.transfer.pop();
        }
    }

    private void connectLoop(CfgNode loopEnd, CfgNode condition) {
        // Delete the edge from the last statement of the loop to the next node.
        Iterator<GraphEdge> i = loopEnd.getOutgoingEdgeSet().iterator();
        ArrayList<GraphEdge> toRemove = new ArrayList<GraphEdge>();
        while (i.hasNext()) {
            toRemove.add(i.next());
        }
        i = toRemove.iterator();
        while (i.hasNext()) {
            currentCfg.remove(i.next());
        }

        currentCfg.addFlow(CfgFlowKind.LOOP_FLOW, loopEnd, condition);
    }

    private void connectReturning(CfgNode exit) {
        ArrayList<GraphEdge> toRemove = new ArrayList<GraphEdge>();
        while (!trace.returning.isEmpty()) {
            // Get the returning node.
            CfgNode node = trace.returning.pop();
            Iterator<GraphEdge> i = node.getOutgoingEdgeSet().iterator();
            toRemove.clear();

            // Remove all the existing outgoing edges of this node.
            while (i.hasNext()) {
                toRemove.add(i.next());
            }
            i = toRemove.iterator();
            while (i.hasNext()) {
                currentCfg.remove(i.next());
            }

            currentCfg.addFlow(CfgFlowKind.RETURN_FLOW, node, exit);
        }
    }

    public void endVisit(AllocationExpression allocationExpression, BlockScope scope) {
        addCallNode();

        path.pop();
    }

    // public void endVisit(AND_AND_Expression and_and_Expression, BlockScope
    // scope) {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    public void endVisit(AnnotationMethodDeclaration annotationTypeDeclaration, ClassScope classScope) {
        // do nothing by default
    }

    public void endVisit(Argument argument, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(ArrayAllocationExpression arrayAllocationExpression, BlockScope scope) {
        addNode(trace.prefetched.pop(), lastNode, currentEnd);
        path.pop();
    }

    public void endVisit(ArrayInitializer arrayInitializer, BlockScope scope) {
        addNode(trace.prefetched.pop(), lastNode, currentEnd);
        path.pop();
    }

    public void endVisit(ArrayQualifiedTypeReference arrayQualifiedTypeReference, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(ArrayQualifiedTypeReference arrayQualifiedTypeReference, ClassScope scope) {
        // do nothing by default
    }

    public void endVisit(ArrayReference arrayReference, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(ArrayTypeReference arrayTypeReference, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(ArrayTypeReference arrayTypeReference, ClassScope scope) {
        // do nothing by default
    }

    public void endVisit(AssertStatement assertStatement, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(Assignment assignment, BlockScope scope) {
        // addNode(prefetchedNodes.pop(), lastNode, currentEnd);
        addNode(trace.prefetched.pop(), lastNode, currentEnd);
        path.pop();
    }

    // public void endVisit(BinaryExpression binaryExpression, BlockScope scope)
    // {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    public void endVisit(Block block, BlockScope scope) {
        path.pop();
    }

    public void endVisit(BreakStatement breakStatement, BlockScope scope) {
        // CfgNode node = prefetchedNodes.pop();
        CfgNode node = trace.prefetched.pop();

        addNode(node, lastNode, currentEnd);
        // currentCfg.addFlow(node, loopInfo.loops.peek());
        // loopInfo.loopTransfer.push(node);
        // currentCfg.addFlow(node, trace.conditions.peek());
        trace.transfer.push(node);

        path.pop();
    }

    public void endVisit(CaseStatement caseStatement, BlockScope scope) {
        // Never visit case statement here!
    }

    // public void endVisit(CastExpression castExpression, BlockScope scope) {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    // public void endVisit(CharLiteral charLiteral, BlockScope scope) {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    public void endVisit(ClassLiteralAccess classLiteral, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(Clinit clinit, ClassScope scope) {
        // do nothing by default
    }

    public void endVisit(CompilationUnitDeclaration compilationUnitDeclaration, CompilationUnitScope scope) {
        path.pop();
    }

    public void endVisit(CompoundAssignment compoundAssignment, BlockScope scope) {
        // addNode(prefetchedNodes.pop(), lastNode, currentEnd);
        addNode(trace.prefetched.pop(), lastNode, currentEnd);
        path.pop();
    }

    // public void endVisit(ConditionalExpression conditionalExpression,
    // BlockScope scope) {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    public void endVisit(ConstructorDeclaration constructorDeclaration, ClassScope scope) {
        currentEnd = trace.prefetched.pop();
        clearAuxiliaryNodes(currentCfg);
        cfgs.put(constructorDeclaration, currentCfg);
        currentCfg = trace.lastCfgs.pop();
        path.pop();
    }

    public void endVisit(ContinueStatement continueStatement, BlockScope scope) {
        // CfgNode node = prefetchedNodes.pop();
        CfgNode node = trace.prefetched.pop();

        addNode(node, lastNode, currentEnd);
        // currentCfg.addFlow(node, loopInfo.loops.peek());
        // loopInfo.loopTransfer.push(node);
        // currentCfg.addFlow(node, trace.conditions.peek());
        trace.transfer.push(node);

        path.pop();
    }

    public void endVisit(DoStatement doStatement, BlockScope scope) {
        // startLoop(doStatement.condition, null);
        // endLoop();
        CfgNode startNode = trace.prefetched.pop();

        // CfgNode cond = startBranch(doStatement.condition, scope); // cond no
        // use.
        startBranch(doStatement.condition, scope);
        CfgNode branchEntry = trace.prefetched.peek();
        trace.prefetched.push(startNode); // For the transfer node connection.
        connectTransfer(branchEntry, currentEnd);
        connectLoop(lastNode, startNode);
        endBranch();

        path.pop();
    }

    // public void endVisit(DoubleLiteral doubleLiteral, BlockScope scope) {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    public void endVisit(EmptyStatement emptyStatement, BlockScope scope) {
        // addNode(prefetchedNodes.pop(), lastNode, currentEnd);
        addNode(trace.prefetched.pop(), lastNode, currentEnd);
        path.pop();
    }

    // public void endVisit(EqualExpression equalExpression, BlockScope scope) {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    public void endVisit(ExplicitConstructorCall explicitConstructor, BlockScope scope) {
        addCallNode();

        path.pop();
    }

    // public void endVisit(ExtendedStringLiteral extendedStringLiteral,
    // BlockScope scope) {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    // public void endVisit(FalseLiteral falseLiteral, BlockScope scope) {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    public void endVisit(FieldDeclaration fieldDeclaration, MethodScope scope) {
        // do nothing by default
    }

    public void endVisit(FieldReference fieldReference, BlockScope scope) {
        // do nothing by default
    }

    // public void endVisit(FloatLiteral floatLiteral, BlockScope scope) {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    public void endVisit(ForeachStatement forStatement, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(ForStatement forStatement, BlockScope scope) {
        connectTransfer(trace.prefetched.peek(), currentEnd);
        connectLoop(lastNode, trace.prefetched.peek());

        endBranch();

        path.pop();
    }

    public void endVisit(IfStatement ifStatement, BlockScope scope) {
        endBranch();

        path.pop();
    }

    public void endVisit(ImportReference importRef, CompilationUnitScope scope) {
        // do nothing by default
    }

    public void endVisit(Initializer initializer, MethodScope scope) {
        // do nothing by default
    }

    // public void endVisit(InstanceOfExpression instanceOfExpression,
    // BlockScope scope) {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    // public void endVisit(IntLiteral intLiteral, BlockScope scope) {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    /*
     * public void endVisit(JavadocArgumentExpression expression, BlockScope
     * scope) { // do nothing by default }
     * 
     * public void endVisit(JavadocArrayQualifiedTypeReference typeRef,
     * BlockScope scope) { // do nothing by default }
     * 
     * public void endVisit(JavadocArraySingleTypeReference typeRef, BlockScope
     * scope) { // do nothing by default }
     * 
     * public void endVisit(JavadocFieldReference fieldRef, BlockScope scope) { //
     * do nothing by default }
     * 
     * public void endVisit(JavadocMessageSend messageSend, BlockScope scope) { //
     * do nothing by default }
     * 
     * public void endVisit(JavadocQualifiedTypeReference typeRef, BlockScope
     * scope) { // do nothing by default }
     * 
     * public void endVisit(JavadocReturnStatement statement, BlockScope scope) { //
     * do nothing by default }
     * 
     * public void endVisit(JavadocSingleNameReference argument, BlockScope
     * scope) { // do nothing by default }
     * 
     * public void endVisit(JavadocSingleTypeReference typeRef, BlockScope
     * scope) { // do nothing by default }
     */

    public void endVisit(LabeledStatement labeledStatement, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(LocalDeclaration localDeclaration, BlockScope scope) {
        addNode(trace.prefetched.pop(), lastNode, currentEnd);
        path.pop();
    }

    // public void endVisit(LongLiteral longLiteral, BlockScope scope) {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    // /**
    // * @param annotation
    // * @param scope
    // * @since 3.1
    // */
    // public void endVisit(MarkerAnnotation annotation, BlockScope scope) {
    // // do nothing by default
    // }
    //
    // /**
    // * @param annotation
    // * @param scope
    // * @since 3.1
    // */
    // public void endVisit(MarkerAnnotation annotation, CompilationUnitScope
    // scope) {
    // // do nothing by default
    // }
    //
    // /**
    // * @param pair
    // * @param scope
    // */
    // public void endVisit(MemberValuePair pair, BlockScope scope) {
    // // do nothing by default
    // }
    //
    // /**
    // * @param pair
    // * @param scope
    // */
    // public void endVisit(MemberValuePair pair, CompilationUnitScope scope) {
    // // do nothing by default
    // }

    public void endVisit(MessageSend messageSend, BlockScope scope) {
        addCallNode();

        path.pop();
    }

    public void endVisit(MethodDeclaration methodDeclaration, ClassScope scope) {
        if (methodDeclaration instanceof PointcutDeclaration) {
            // Do nothing.
        } else {
            currentEnd = trace.prefetched.pop();
            connectReturning(currentCfg.getCfgReturningExit());
            clearAuxiliaryNodes(currentCfg);
            cfgs.put(methodDeclaration, currentCfg);
            currentCfg = trace.lastCfgs.pop();
        }
        path.pop();
    }

    // public void endVisit(StringLiteralConcatenation literal, BlockScope
    // scope) {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    // /**
    // * @param annotation
    // * @param scope
    // * @since 3.1
    // */
    // public void endVisit(NormalAnnotation annotation, BlockScope scope) {
    // // do nothing by default
    // }
    //
    // /**
    // * @param annotation
    // * @param scope
    // * @since 3.1
    // */
    // public void endVisit(NormalAnnotation annotation, CompilationUnitScope
    // scope) {
    // // do nothing by default
    // }

    // public void endVisit(NullLiteral nullLiteral, BlockScope scope) {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    // public void endVisit(OR_OR_Expression or_or_Expression, BlockScope scope)
    // {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    public void endVisit(ParameterizedQualifiedTypeReference parameterizedQualifiedTypeReference, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(ParameterizedQualifiedTypeReference parameterizedQualifiedTypeReference, ClassScope scope) {
        // do nothing by default
    }

    public void endVisit(ParameterizedSingleTypeReference parameterizedSingleTypeReference, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(ParameterizedSingleTypeReference parameterizedSingleTypeReference, ClassScope scope) {
        // do nothing by default
    }

    public void endVisit(PostfixExpression postfixExpression, BlockScope scope) {
        addNode(trace.prefetched.pop(), lastNode, currentEnd);
        path.pop();
    }

    public void endVisit(PrefixExpression prefixExpression, BlockScope scope) {
        addNode(trace.prefetched.pop(), lastNode, currentEnd);
        path.pop();
    }

    public void endVisit(QualifiedAllocationExpression qualifiedAllocationExpression, BlockScope scope) {
        addNode(trace.prefetched.pop(), lastNode, currentEnd);
        path.pop();
    }

    public void endVisit(QualifiedNameReference qualifiedNameReference, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(QualifiedSuperReference qualifiedSuperReference, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(QualifiedThisReference qualifiedThisReference, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(QualifiedTypeReference qualifiedTypeReference, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(QualifiedTypeReference qualifiedTypeReference, ClassScope scope) {
        // do nothing by default
    }

    public void endVisit(ReturnStatement returnStatement, BlockScope scope) {
        CfgNode node = trace.prefetched.pop();

        addNode(node, lastNode, currentEnd);
        trace.returning.push(node);

        path.pop();
    }

    // /**
    // * @param annotation
    // * @param scope
    // * @since 3.1
    // */
    // public void endVisit(SingleMemberAnnotation annotation, BlockScope scope)
    // {
    // // do nothing by default
    // }
    //
    // /**
    // * @param annotation
    // * @param scope
    // * @since 3.1
    // */
    // public void endVisit(SingleMemberAnnotation annotation,
    // CompilationUnitScope scope) {
    // // do nothing by default
    // }

    public void endVisit(SingleNameReference singleNameReference, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(SingleTypeReference singleTypeReference, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(SingleTypeReference singleTypeReference, ClassScope scope) {
        // do nothing by default
    }

    // public void endVisit(StringLiteral stringLiteral, BlockScope scope) {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    public void endVisit(SuperReference superReference, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(SwitchStatement switchStatement, BlockScope scope) {
        connectTransfer(trace.prefetched.peek(), currentEnd);
        lastNode = currentEnd;
        currentEnd = trace.prefetched.pop();

        path.pop();
    }

    // public void endVisit(SynchronizedStatement synchronizedStatement,
    // BlockScope scope) {
    // // do nothing by default
    // }

    public void endVisit(ThisReference thisReference, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(ThrowStatement throwStatement, BlockScope scope) {
        // do nothing by default
    }

    // public void endVisit(TrueLiteral trueLiteral, BlockScope scope) {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    public void endVisit(TryStatement tryStatement, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
        path.pop();
    }

    public void endVisit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
        path.pop();
    }

    public void endVisit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
        path.pop();
    }

    public void endVisit(TypeParameter typeParameter, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(TypeParameter typeParameter, ClassScope scope) {
        // do nothing by default
    }

    // public void endVisit(UnaryExpression unaryExpression, BlockScope scope) {
    // addNode(trace.prefetched.pop(), lastNode, currentEnd);
    // path.pop();
    // }

    public void endVisit(WhileStatement whileStatement, BlockScope scope) {
        connectTransfer(trace.prefetched.peek(), currentEnd);
        connectLoop(lastNode, trace.prefetched.peek());

        endBranch();

        path.pop();
    }

    public void endVisit(Wildcard wildcard, BlockScope scope) {
        // do nothing by default
    }

    public void endVisit(Wildcard wildcard, ClassScope scope) {
        // do nothing by default
    }

    public boolean visit(AllocationExpression allocationExpression, BlockScope scope) {
        path.push(allocationExpression);

        CfgCall calling = new CfgCall(CfgNodeKind.CALL);
        calling.setASTNode(allocationExpression);
        CfgReturn returning = new CfgReturn();
        calling.setReturnNode(returning);
        returning.setCallNode(calling);
        trace.prefetched.push(returning);
        trace.prefetched.push(calling);

        return true;
    }

    // public boolean visit(AND_AND_Expression and_and_Expression, BlockScope
    // scope) {
    // path.push(and_and_Expression);
    //
    // CfgExpression node = new CfgExpression(CfgNodeKind.AND_AND_EXPRESSION);
    // node.setASTNode(and_and_Expression);
    // trace.prefetched.push(node);
    //
    // return true;
    // }

    public boolean visit(AnnotationMethodDeclaration annotationTypeDeclaration, ClassScope classScope) {
        return false;
    }

    public boolean visit(Argument argument, BlockScope scope) {
        return false;
    }

    public boolean visit(ArrayAllocationExpression arrayAllocationExpression, BlockScope scope) {
        path.push(arrayAllocationExpression);

        CfgExpression node = new CfgExpression(CfgNodeKind.ARRAY_ALLOCATION_EXPRESSION);
        node.setASTNode(arrayAllocationExpression);
        trace.prefetched.push(node);

        return true;
    }

    public boolean visit(ArrayInitializer arrayInitializer, BlockScope scope) {
        path.push(arrayInitializer);

        CfgExpression node = new CfgExpression(CfgNodeKind.ARRAY_INITIALIZER_EXPRESSION);
        node.setASTNode(arrayInitializer);
        trace.prefetched.push(node);

        return true;
    }

    public boolean visit(ArrayQualifiedTypeReference arrayQualifiedTypeReference, BlockScope scope) {
        return false;
    }

    public boolean visit(ArrayQualifiedTypeReference arrayQualifiedTypeReference, ClassScope scope) {
        return false;
    }

    public boolean visit(ArrayReference arrayReference, BlockScope scope) {
        return false;
    }

    public boolean visit(ArrayTypeReference arrayTypeReference, BlockScope scope) {
        return false;
    }

    public boolean visit(ArrayTypeReference arrayTypeReference, ClassScope scope) {
        return false;
    }

    public boolean visit(AssertStatement assertStatement, BlockScope scope) {
        return false;
    }

    public boolean visit(Assignment assignment, BlockScope scope) {
        // check(assignment);
        path.push(assignment);

        CfgStatement node = new CfgStatement(CfgNodeKind.ASSIGNMENT);
        node.setASTNode(assignment);
        // prefetchedNodes.push(node);
        trace.prefetched.push(node);

        return true;
    }

    // public boolean visit(BinaryExpression binaryExpression, BlockScope scope)
    // {
    // path.push(binaryExpression);
    //
    // CfgExpression node = new CfgExpression(CfgNodeKind.BINARY_EXPRESSION);
    // node.setASTNode(binaryExpression);
    // trace.prefetched.push(node);
    //
    // return true;
    // }

    public boolean visit(Block block, BlockScope scope) {
        // check(block);
        path.push(block);

        /*
         * CfgAuxiliaryNode node = new CfgAuxiliaryNode(
         * CfgNodeKind.CFG_AUXILIARY_NODE); addNode(node,
         * CfgFlowKind.NORMAL_FLOW, CfgFlowKind.NORMAL_FLOW);
         * prefetchedNodes.push(node);
         */

        return true;
    }

    public boolean visit(BreakStatement breakStatement, BlockScope scope) {
        // check(breakStatement);
        path.push(breakStatement);

        CfgStatement node = new CfgStatement(CfgNodeKind.BREAK_STATEMENT);
        node.setASTNode(breakStatement);
        // prefetchedNodes.push(node);
        trace.prefetched.push(node);

        return true;
    }

    public boolean visit(CaseStatement caseStatement, BlockScope scope) {
        // Never visit case statement here!
        return false;
    }

    // public boolean visit(CastExpression castExpression, BlockScope scope) {
    // path.push(castExpression);
    //
    // CfgExpression node = new CfgExpression(CfgNodeKind.CAST_EXPRESSION);
    // node.setASTNode(castExpression);
    // trace.prefetched.push(node);
    //
    // return true;
    // }

    // public boolean visit(CharLiteral charLiteral, BlockScope scope) {
    // path.push(charLiteral);
    //        
    // CfgExpression node = new CfgExpression(CfgNodeKind.EXPRESSION);
    // node.setASTNode(charLiteral);
    // trace.prefetched.push(node);
    //        
    // return true;
    // }

    public boolean visit(ClassLiteralAccess classLiteral, BlockScope scope) {
        return false;
    }

    public boolean visit(Clinit clinit, ClassScope scope) {
        return false;
    }

    public boolean visit(CompilationUnitDeclaration compilationUnitDeclaration, CompilationUnitScope scope) {
        path.push(compilationUnitDeclaration);

        return true;
    }

    public boolean visit(CompoundAssignment compoundAssignment, BlockScope scope) {
        // check(compoundAssignment);
        path.push(compoundAssignment);

        CfgStatement node = new CfgStatement(CfgNodeKind.ASSIGNMENT);
        node.setASTNode(compoundAssignment);
        // prefetchedNodes.push(node);
        trace.prefetched.push(node);

        return true;
    }

    // public boolean visit(ConditionalExpression conditionalExpression,
    // BlockScope scope) {
    // path.push(conditionalExpression);
    //
    // CfgExpression node = new
    // CfgExpression(CfgNodeKind.CONDITIONAL_EXPRESSION);
    // node.setASTNode(conditionalExpression);
    // trace.prefetched.push(node);
    //
    // return true;
    // }

    public boolean visit(ConstructorDeclaration constructorDeclaration, ClassScope scope) {
        path.push(constructorDeclaration);

        trace.lastCfgs.push(currentCfg);
        currentCfg = new ControlFlowGraph(ControlFlowGraph.CONSTRUCTOR_CFG);
        lastNode = currentCfg.getCfgEntry();
        trace.prefetched.push(currentEnd);
        currentEnd = currentCfg.getCfgReturningExit();

        return true;
    }

    public boolean visit(ContinueStatement continueStatement, BlockScope scope) {
        // check(continueStatement);
        path.push(continueStatement);

        CfgStatement node = new CfgStatement(CfgNodeKind.CONTINUE_STATEMENT);
        node.setASTNode(continueStatement);
        // prefetchedNodes.push(node);
        trace.prefetched.push(node);

        return true;
    }

    public boolean visit(DoStatement doStatement, BlockScope scope) {
        // check(doStatement);
        path.push(doStatement);

        CfgNode node = new CfgAuxiliaryNode(CfgNodeKind.AUXILIARY_NODE);
        addNode(node, lastNode, currentEnd);
        trace.prefetched.push(node);
        trace.transfer.push(node);

        if (doStatement.action != null) {
            doStatement.action.traverse(this, scope);
        }

        return false;
    }

    // public boolean visit(DoubleLiteral doubleLiteral, BlockScope scope) {
    // path.push(doubleLiteral);
    //        
    // CfgExpression node = new CfgExpression(CfgNodeKind.EXPRESSION);
    // node.setASTNode(doubleLiteral);
    // trace.prefetched.push(node);
    //        
    // return true;
    // }

    public boolean visit(EmptyStatement emptyStatement, BlockScope scope) {
        // check(emptyStatement);
        path.push(emptyStatement);

        CfgStatement node = new CfgStatement(CfgNodeKind.EMPTY_STATEMENT);
        node.setASTNode(emptyStatement);
        // prefetchedNodes.push(node);
        trace.prefetched.push(node);

        return true;
    }

    // public boolean visit(EqualExpression equalExpression, BlockScope scope) {
    // path.push(equalExpression);
    //
    // CfgExpression node = new CfgExpression(CfgNodeKind.EQUAL_EXPRESSION);
    // node.setASTNode(equalExpression);
    // trace.prefetched.push(node);
    //
    // return true;
    // }

    public boolean visit(ExplicitConstructorCall explicitConstructor, BlockScope scope) {
        path.push(explicitConstructor);

        CfgCall calling = new CfgCall(CfgNodeKind.CALL);
        calling.setASTNode(explicitConstructor);
        CfgReturn returning = new CfgReturn();
        calling.setReturnNode(returning);
        returning.setCallNode(calling);
        trace.prefetched.push(returning);
        trace.prefetched.push(calling);

        return true;
    }

    // public boolean visit(ExtendedStringLiteral extendedStringLiteral,
    // BlockScope scope) {
    // path.push(extendedStringLiteral);
    //        
    // CfgExpression node = new CfgExpression(CfgNodeKind.EXPRESSION);
    // node.setASTNode(extendedStringLiteral);
    // trace.prefetched.push(node);
    //        
    // return true;
    // }

    // public boolean visit(FalseLiteral falseLiteral, BlockScope scope) {
    // path.push(falseLiteral);
    //        
    // CfgExpression node = new CfgExpression(CfgNodeKind.EXPRESSION);
    // node.setASTNode(falseLiteral);
    // trace.prefetched.push(node);
    //        
    // return true;
    // }

    public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
        return false;
    }

    public boolean visit(FieldReference fieldReference, BlockScope scope) {
        return false;
    }

    // public boolean visit(FloatLiteral floatLiteral, BlockScope scope) {
    // path.push(floatLiteral);
    //        
    // CfgExpression node = new CfgExpression(CfgNodeKind.EXPRESSION);
    // node.setASTNode(floatLiteral);
    // trace.prefetched.push(node);
    //        
    // return true;
    // }

    public boolean visit(ForeachStatement forStatement, BlockScope scope) {
        return false;
    }

    public boolean visit(ForStatement forStatement, BlockScope scope) {
        path.push(forStatement);

        if (forStatement.initializations != null) {
            for (int i = 0; i < forStatement.initializations.length; ++i) {
                forStatement.initializations[i].traverse(this, scope);
            }
        }

        // CfgNode cond = startBranch(forStatement.condition, scope); // cond no
        // use!
        startBranch(forStatement.condition, scope);
        CfgNode branchEntry = trace.prefetched.peek();
        trace.prefetched.push(branchEntry);
        trace.transfer.push(branchEntry);

        if (forStatement.action != null) {
            forStatement.action.traverse(this, scope);
        }

        if (forStatement.increments != null) {
            for (int i = 0; i < forStatement.increments.length; ++i) {
                forStatement.increments[i].traverse(this, scope);
            }
        }

        return false; // do nothing by default, keep traversing
    }

    public boolean visit(IfStatement ifStatement, BlockScope scope) {
        // check(ifStatement);
        path.push(ifStatement);

        CfgNode cond = startBranch(ifStatement.condition, scope);
        Iterator<GraphEdge> i = cond.getOutgoingEdgeSet().iterator();
        while (i.hasNext()) {
            CfgFlow f = (CfgFlow) i.next();
            lastNode = f.getDestinationNode();
            if (f.getKind() == CfgFlowKind.TRUE_FLOW && ifStatement.thenStatement != null) {
                ifStatement.thenStatement.traverse(this, scope);
            } else if (f.getKind() == CfgFlowKind.FALSE_FLOW && ifStatement.elseStatement != null) {
                ifStatement.elseStatement.traverse(this, scope);
            }
        }

        return false;
    }

    public boolean visit(ImportReference importRef, CompilationUnitScope scope) {
        return false;
    }

    public boolean visit(Initializer initializer, MethodScope scope) {
        return false;
    }

    // public boolean visit(InstanceOfExpression instanceOfExpression,
    // BlockScope scope) {
    // path.push(instanceOfExpression);
    //
    // CfgExpression node = new
    // CfgExpression(CfgNodeKind.INSTANCE_OF_EXPRESSION);
    // node.setASTNode(instanceOfExpression);
    // trace.prefetched.push(node);
    //
    // return true;
    // }

    // public boolean visit(IntLiteral intLiteral, BlockScope scope) {
    // path.push(intLiteral);
    //        
    // CfgExpression node = new CfgExpression(CfgNodeKind.EXPRESSION);
    // node.setASTNode(intLiteral);
    // trace.prefetched.push(node);
    //        
    // return true;
    // }

    /*
     * public boolean visit(JavadocArgumentExpression expression, BlockScope
     * scope) { return true; // do nothing by default, keep traversing }
     * 
     * public boolean visit(JavadocArrayQualifiedTypeReference typeRef,
     * BlockScope scope) { return true; // do nothing by default, keep
     * traversing }
     * 
     * public boolean visit(JavadocArraySingleTypeReference typeRef, BlockScope
     * scope) { return true; // do nothing by default, keep traversing }
     * 
     * public boolean visit(JavadocFieldReference fieldRef, BlockScope scope) {
     * return true; // do nothing by default, keep traversing }
     * 
     * public boolean visit(JavadocMessageSend messageSend, BlockScope scope) {
     * return true; // do nothing by default, keep traversing }
     * 
     * public boolean visit(JavadocQualifiedTypeReference typeRef, BlockScope
     * scope) { return true; // do nothing by default, keep traversing }
     * 
     * public boolean visit(JavadocReturnStatement statement, BlockScope scope) {
     * return true; // do nothing by default, keep traversing }
     * 
     * public boolean visit(JavadocSingleNameReference argument, BlockScope
     * scope) { return true; // do nothing by default, keep traversing }
     * 
     * public boolean visit(JavadocSingleTypeReference typeRef, BlockScope
     * scope) { return true; // do nothing by default, keep traversing }
     */

    public boolean visit(LabeledStatement labeledStatement, BlockScope scope) {
        return false;
    }

    public boolean visit(LocalDeclaration localDeclaration, BlockScope scope) {
        path.push(localDeclaration);

        CfgStatement node = new CfgStatement(CfgNodeKind.VARIABLE_DECLARATION);
        node.setASTNode(localDeclaration);
        trace.prefetched.push(node);

        return true;
    }

    // public boolean visit(LongLiteral longLiteral, BlockScope scope) {
    // path.push(longLiteral);
    //        
    // CfgExpression node = new CfgExpression(CfgNodeKind.EXPRESSION);
    // node.setASTNode(longLiteral);
    // trace.prefetched.push(node);
    //        
    // return true;
    // }

    // /**
    // * @param annotation
    // * @param scope
    // * @since 3.1
    // */
    // public boolean visit(MarkerAnnotation annotation, BlockScope scope) {
    // return true;
    // }
    //
    // /**
    // * @param annotation
    // * @param scope
    // * @since 3.1
    // */
    // public boolean visit(MarkerAnnotation annotation, CompilationUnitScope
    // scope) {
    // return true;
    // }
    //
    // /**
    // * @param pair
    // * @param scope
    // * @since 3.1
    // */
    // public boolean visit(MemberValuePair pair, BlockScope scope) {
    // return true;
    // }
    //
    // /**
    // * @param pair
    // * @param scope
    // * @since 3.1
    // */
    // public boolean visit(MemberValuePair pair, CompilationUnitScope scope) {
    // return true;
    // }

    public boolean visit(MessageSend messageSend, BlockScope scope) {
        path.push(messageSend);

        CfgCall calling = new CfgCall(CfgNodeKind.CALL);
        calling.setASTNode(messageSend);
        CfgReturn returning = new CfgReturn();
        calling.setReturnNode(returning);
        returning.setCallNode(calling);
        trace.prefetched.push(returning);
        trace.prefetched.push(calling);

        return true;
    }

    public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
        path.push(methodDeclaration);

        if (methodDeclaration instanceof PointcutDeclaration) {
            return false;
        }

        trace.lastCfgs.push(currentCfg);
        currentCfg = new ControlFlowGraph(ControlFlowGraph.METHOD_CFG);
        lastNode = currentCfg.getCfgEntry();
        trace.prefetched.push(currentEnd);
        currentEnd = currentCfg.getCfgReturningExit();

        return true;
    }

    // public boolean visit(StringLiteralConcatenation literal, BlockScope
    // scope) {
    // path.push(literal);
    //        
    // CfgExpression node = new CfgExpression(CfgNodeKind.EXPRESSION);
    // node.setASTNode(literal);
    // trace.prefetched.push(node);
    //        
    // return true;
    // }

    // /**
    // * @param annotation
    // * @param scope
    // * @since 3.1
    // */
    // public boolean visit(NormalAnnotation annotation, BlockScope scope) {
    // return true;
    // }
    //
    // /**
    // * @param annotation
    // * @param scope
    // * @since 3.1
    // */
    // public boolean visit(NormalAnnotation annotation, CompilationUnitScope
    // scope) {
    // return true;
    // }

    // public boolean visit(NullLiteral nullLiteral, BlockScope scope) {
    // path.push(nullLiteral);
    //        
    // CfgExpression node = new CfgExpression(CfgNodeKind.EXPRESSION);
    // node.setASTNode(nullLiteral);
    // trace.prefetched.push(node);
    //        
    // return true;
    // }

    // public boolean visit(OR_OR_Expression or_or_Expression, BlockScope scope)
    // {
    // path.push(or_or_Expression);
    //
    // CfgExpression node = new CfgExpression(CfgNodeKind.OR_OR_EXPRESSION);
    // node.setASTNode(or_or_Expression);
    // trace.prefetched.push(node);
    //
    // return true;
    // }

    public boolean visit(ParameterizedQualifiedTypeReference parameterizedQualifiedTypeReference, BlockScope scope) {
        return false;
    }

    public boolean visit(ParameterizedQualifiedTypeReference parameterizedQualifiedTypeReference, ClassScope scope) {
        return false;
    }

    public boolean visit(ParameterizedSingleTypeReference parameterizedSingleTypeReference, BlockScope scope) {
        return false;
    }

    public boolean visit(ParameterizedSingleTypeReference parameterizedSingleTypeReference, ClassScope scope) {
        return false;
    }

    public boolean visit(PostfixExpression postfixExpression, BlockScope scope) {
        path.push(postfixExpression);

        CfgStatement node = new CfgStatement(CfgNodeKind.POSTFIX_EXPRESSION);
        node.setASTNode(postfixExpression);
        trace.prefetched.push(node);

        return true;
    }

    public boolean visit(PrefixExpression prefixExpression, BlockScope scope) {
        path.push(prefixExpression);

        CfgStatement node = new CfgStatement(CfgNodeKind.PREFIX_EXPRESSION);
        node.setASTNode(prefixExpression);
        trace.prefetched.push(node);

        return true;
    }

    public boolean visit(QualifiedAllocationExpression qualifiedAllocationExpression, BlockScope scope) {
        path.push(qualifiedAllocationExpression);

        CfgExpression node = new CfgExpression(CfgNodeKind.QUALIFIED_ALLOCATION_EXPRESSION);
        node.setASTNode(qualifiedAllocationExpression);
        trace.prefetched.push(node);

        return true;
    }

    public boolean visit(QualifiedNameReference qualifiedNameReference, BlockScope scope) {
        return false;
    }

    public boolean visit(QualifiedSuperReference qualifiedSuperReference, BlockScope scope) {
        return false;
    }

    public boolean visit(QualifiedThisReference qualifiedThisReference, BlockScope scope) {
        return false;
    }

    public boolean visit(QualifiedTypeReference qualifiedTypeReference, BlockScope scope) {
        return false;
    }

    public boolean visit(QualifiedTypeReference qualifiedTypeReference, ClassScope scope) {
        return false;
    }

    public boolean visit(ReturnStatement returnStatement, BlockScope scope) {
        path.push(returnStatement);

        CfgStatement node = new CfgStatement(CfgNodeKind.RETURN_STATEMENT);
        node.setASTNode(returnStatement);
        trace.prefetched.push(node);

        return true;
    }

    // /**
    // * @param annotation
    // * @param scope
    // * @since 3.1
    // */
    // public boolean visit(SingleMemberAnnotation annotation, BlockScope scope)
    // {
    // return true;
    // }
    //
    // /**
    // * @param annotation
    // * @param scope
    // * @since 3.1
    // */
    // public boolean visit(SingleMemberAnnotation annotation,
    // CompilationUnitScope scope) {
    // return true;
    // }

    public boolean visit(SingleNameReference singleNameReference, BlockScope scope) {
        return false;
    }

    public boolean visit(SingleTypeReference singleTypeReference, BlockScope scope) {
        return false;
    }

    public boolean visit(SingleTypeReference singleTypeReference, ClassScope scope) {
        return false;
    }

    // public boolean visit(StringLiteral stringLiteral, BlockScope scope) {
    // path.push(stringLiteral);
    //        
    // CfgExpression node = new CfgExpression(CfgNodeKind.EXPRESSION);
    // node.setASTNode(stringLiteral);
    // trace.prefetched.push(node);
    //        
    // return true;
    // }

    public boolean visit(SuperReference superReference, BlockScope scope) {
        return false;
    }

    public boolean visit(SwitchStatement switchStatement, BlockScope scope) {
        path.push(switchStatement);

        if (switchStatement.expression != null) {
            switchStatement.expression.traverse(this, scope);
        }
        CfgExpression cond = new CfgExpression(CfgNodeKind.BRANCH_EXPRESSION);
        cond.setASTNode(switchStatement.expression);
        addNode(cond, lastNode, currentEnd);
        CfgAuxiliaryNode node = new CfgAuxiliaryNode(CfgNodeKind.AUXILIARY_NODE);
        addNode(node, lastNode, currentEnd);

        trace.prefetched.push(currentEnd);
        currentEnd = node;
        lastNode = cond;
        trace.prefetched.push(cond);
        trace.transfer.push(cond);

        if (switchStatement.statements != null) {
            CfgNode tmp = null;
            ArrayList<Statement> defaultStatements = new ArrayList<Statement>();
            boolean isInDefault = false;
            for (int i = 0; i < switchStatement.statements.length; ++i) {
                Statement statement = switchStatement.statements[i];
                if (statement instanceof CaseStatement) {
                    if (statement == switchStatement.defaultCase) {
                        isInDefault = true;
                        defaultStatements.add(statement);
                    } else {
                        isInDefault = false;
                        // If it is not in default case, make a branch.
                        if (tmp != null) {
                            endBranch();
                            tmp = null;
                        }
                        tmp = startBranch(statement, scope);
                    }
                } else {
                    if (isInDefault) {
                        defaultStatements.add(statement);
                    } else {
                        statement.traverse(this, switchStatement.scope);
                    }
                }
            }
            if (tmp != null) {
                endBranch();
                tmp = null;
            }
            // Make default case part.
            if (!defaultStatements.isEmpty()) {
                Iterator<Statement> i = defaultStatements.iterator();

                // Make the default case statement node.
                CfgStatement defaultStatement = new CfgStatement(CfgNodeKind.CASE_STATEMENT);
                defaultStatement.setASTNode(i.next());
                addNode(defaultStatement, lastNode, currentEnd);

                while (i.hasNext()) {
                    Statement s = i.next();

                    // Traverse all the statements in the default case.
                    s.traverse(this, switchStatement.scope);
                }
            }
        }

        return false;
    }

    // public boolean visit(SynchronizedStatement synchronizedStatement,
    // BlockScope scope) {
    // return true; // do nothing by default, keep traversing
    // }

    public boolean visit(ThisReference thisReference, BlockScope scope) {
        return false;
    }

    public boolean visit(ThrowStatement throwStatement, BlockScope scope) {
        return false;
    }

    // public boolean visit(TrueLiteral trueLiteral, BlockScope scope) {
    // path.push(trueLiteral);
    //        
    // CfgExpression node = new CfgExpression(CfgNodeKind.EXPRESSION);
    // node.setASTNode(trueLiteral);
    // trace.prefetched.push(node);
    //        
    // return true;
    // }

    public boolean visit(TryStatement tryStatement, BlockScope scope) {
        return false;
    }

    public boolean visit(TypeDeclaration localTypeDeclaration, BlockScope scope) {
        path.push(localTypeDeclaration);

        return true;
    }

    public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
        path.push(memberTypeDeclaration);

        return true;
    }

    public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
        path.push(typeDeclaration);

        return true;
    }

    public boolean visit(TypeParameter typeParameter, BlockScope scope) {
        return false;
    }

    public boolean visit(TypeParameter typeParameter, ClassScope scope) {
        return false;
    }

    // public boolean visit(UnaryExpression unaryExpression, BlockScope scope) {
    // path.push(unaryExpression);
    //
    // CfgExpression node = new CfgExpression(CfgNodeKind.UNARY_EXPRESSION);
    // node.setASTNode(unaryExpression);
    // trace.prefetched.push(node);
    //
    // return true;
    // }

    public boolean visit(WhileStatement whileStatement, BlockScope scope) {
        // check(whileStatement);
        path.push(whileStatement);

        // CfgNode cond = startBranch(whileStatement.condition, scope); // cond
        // no use!
        startBranch(whileStatement.condition, scope);
        CfgNode branchEntry = trace.prefetched.peek();
        // loopInfo.loops.push(cond);
        // loopInfo.loopTransfer.push(cond);
        trace.prefetched.push(branchEntry);
        trace.transfer.push(branchEntry);

        // <code>lastNode</code> has been the action.
        if (whileStatement.action != null) {
            whileStatement.action.traverse(this, scope);
        }

        return false;
    }

    public boolean visit(Wildcard wildcard, BlockScope scope) {
        return false;
    }

    public boolean visit(Wildcard wildcard, ClassScope scope) {
        return false;
    }
}
